Skip to content

feat(claude): emit plugin dependencies (#187 slice 1) - #290

Merged
ScriptedAlchemy merged 1 commit into
mainfrom
feat/187-s1-deps
Sep 2, 2026
Merged

feat(claude): emit plugin dependencies (#187 slice 1)#290
ScriptedAlchemy merged 1 commit into
mainfrom
feat/187-s1-deps

Conversation

@ScriptedAlchemy

Copy link
Copy Markdown
Owner

Summary

  • Adds the host-scoped claude.dependencies config surface: the documented union of bare plugin-name strings and closed { name, version?, marketplace? } objects, validated at plan time and emitted into the generated .claude-plugin/plugin.json dependencies array preserving authored order.
  • Nine fresh claude.dependencies.* diagnostics with recovery text: declaration/entry shape, closed object fields, plugin-name pattern (pinned to the manifest's existing lowercase kebab-case rule, recorded in PROVENANCE), duplicate name+marketplace pairs, self-dependency rejection, marketplace shape, and semver-range syntax. The range validator accepts the documented npm-style grammar (~, ^, >=, <=, >, <, =, bare/partial versions, x-wildcards, hyphen ranges, comparator intersections, || unions, pre-release opt-in like ^2.0.0-0) without resolving versions — an invalid range would otherwise surface host-side as range-conflict only after distribution.
  • Pinned plugin schema extended with a closed dependency definition (PROVENANCE notes record that range grammar stays plan-time validation because JSON Schema cannot honestly encode npm range syntax); schema rehash covers the userConfig + dependencies union after rebase.
  • Capability row dependencies driven by new pinned plugin.dependencies facts (entry forms, semver/pre-release rules, {name}--v{version} tag convention, auto-install with command-source/headersHelper exceptions, root-marketplace allowCrossMarketplaceDependenciesOn allowlisting, range intersection, prune, and the four documented error codes) with six dated 2026-09-01 evidence lines incl. the local 2.1.257 host proof; honest unavailable mirror on the unified bundle. Marketplace-side allowCrossMarketplaceDependenciesOn emission is intentionally deferred to the slice-3 marketplace pass.
  • Adapter revisions adjudicated on rebase over feat(claude): emit userConfig manifest options (#187 slice 1) #287: claude 1.9.0, unified plugin 1.8.0.

Part of #187 slice 1 (systematic Claude host-parity pass).

Test plan

  • Scoped suites incl. native host proofs for dependencies, userConfig, settings, and bin (post-rebase): 266 passed, 0 failed, 0 skipped against the installed Claude Code 2.1.257
  • pnpm typecheck
  • pnpm lint (0 errors, 0 warnings, 977 files)

Validate Claude dependency declarations before packaging and preserve them in native and unified plugin manifests with pinned host evidence.
@ScriptedAlchemy
ScriptedAlchemy merged commit 9c767c9 into main Sep 2, 2026
@changeset-bot

changeset-bot Bot commented Sep 2, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: dac4f27

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
agent-bundle Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 2, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-02T06:23:30.576340Z dac4f27 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: dac4f278bf

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


const plugin = {
author: { name: model.metadata.name },
...(dependencies.document === undefined ? {} : { dependencies: dependencies.document }),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Emit marketplace metadata needed to resolve dependencies

On a fresh installation using the generated INSTALL.md, neither dependency form is generally auto-installable: bare names resolve in the generated declaring marketplace, whose plugins array contains only the declaring plugin, while cross-marketplace objects require the root marketplace's allowCrossMarketplaceDependenciesOn field according to claude-2.1.250.json, but the emitted marketplace and its schema omit that field. The manifest therefore advertises dependencies that become dependency-unsatisfied or are blocked unless users manually install them first; emit the required marketplace entries/allowlist or reject declarations the generated distribution cannot resolve.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 2530cc32. The allowlist half was fixed by #310; this commit fixes the bare-name prong by validating same-marketplace names against the emitted plugin list and raising claude.dependencies.unresolved before an unresolvable declaration ships.

Comment on lines +579 to +585
if (name === model.metadata.name) {
diagnostics.push(dependencyDiagnostic(
'claude.dependencies.self',
`Claude plugin ${JSON.stringify(model.metadata.name)} cannot depend on itself.`,
'Remove the self-dependency; self-dependencies can deadlock plugin enable and disable operations.',
));
continue;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Allow same-named plugins from another marketplace

When a plugin such as review-tools declares { marketplace: "acme-shared", name: "review-tools" }, this unconditional name comparison reports a self-dependency even though the explicit marketplace gives the dependency a different identity. The implementation itself deduplicates by marketplace/name pairs, and only an omitted marketplace resolves beside the declaring plugin, so the self check should account for the marketplace rather than rejecting every cross-marketplace name collision.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 2530cc32. The self-dependency diagnostic now applies only to same-marketplace resolution, so { marketplace: "acme-shared", name: "<own-name>" } is preserved and covered.

Comment on lines +428 to +433
export const isValidClaudeDependencyRange = (value: string): boolean => {
if (value.length === 0 || value.trim() !== value) return false;
for (const clause of value.split('||')) {
const range = clause.trim();
if (range.length === 0) return false;
if (hyphenRangePattern.test(range)) continue;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Accept standalone npm wildcard ranges

For a dependency version of "*" or "x", which are valid npm-style semver ranges, semverRangeVersion requires a leading numeric identifier, so this validator returns false and suppresses the entire dependency document with claude.dependencies.version.invalid. Since the new surface promises npm-style ranges and explicitly describes x-wildcards, accept standalone wildcard ranges rather than rejecting valid host input.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 2530cc32. isValidClaudeDependencyRange now accepts standalone *, x, and X clauses, with direct regression cases.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant